<?php

	/** FPDF Graph - Version 1.0.20090717
	 * To be used together with FPDF.
	 * Component that lets you create and print graphs on PDF files.
	 * http://www.fpdf.org
	 * http://code.google.com/p/fpdfreport
	 * 
	 * @author Iván Bustamante
	 * @modified 7 July 2009 by Iván Bustamante
	 * @category PDF Creation
	 * @license GPL v3
	 * @version 1.0.20090717
	 *  
	 **/

	class FPDFGraph {
		private $x = 30;
		private $y = 10;
		private $width = 100;
		private $height = 100;
		private $series = array();
		private $xLabels = array();
		private $yLabels = array();
		private $type = 'line';
		private $xMax = 100;
		private $xMin = 0;
		private $yMax = 100;
		private $yMin = 0;
		private $tickSize = 5;
		private $xPadding = 10;
		private $yPadding = 0;
		private $gridVerticalLines = 10;
		private $gridHorizontalLines = 10;
		private $gridColour = array(200, 200, 200);
		private $gridLineWidth = 0.5;
		private $boxLineWidth = 0.5;
		private $boxColour = array(0, 0, 0);
		private $axisFont = "Arial";
		private $axisFontSize = 8;
		
		public function addSeries($title, $contents){
			$this->series[$title] = $contents;
		}
		
		public function setXY($x, $y){
			$this->x = $x;
			$this->y = $y;
		}
		
		public function setLabelsX($labels){
			$this->xLabels = $labels;
		}
		
		public function setLabelsY($labels){
			$this->yLabels = $labels;
		}
		
		public function setType($type){
			$this->type = $type;
		}
		
		public function setSize($width, $height){
			$this->width = $width;
			$this->height = $height;
		}
		
		public function setGridLines($x, $y){
			$this->gridVerticalLines = $x;
			$this->gridHorizontalLines = $y;
		}
		
		public function setGridLineWidth($width){
			$this->gridLineWidth = $width;
		}
		
		public function setGridColour($colour){
			$this->gridColour = $colour;
		}
		
		public function setBoxLineWidth($width){
			$this->boxLineWidth = $width;
		}
		
		public function setBoxColour($colour){
			$this->boxColour = $colour;
		}
		
		public function setPadding($xPadding, $yPadding){
			$this->xPadding = $xPadding;
			$this->yPadding = $yPadding;
		}
		
		public function setAxisFont($font){
			$this->axisFont = $font;
		}
		
		public function setAxisFontSize($size){
			$this->axisFontSize = $size;
		}
		
		public function calculateMargins(){
			$xMax = -200000000;
			$xMin = 200000000;
			$yMax = -200000000;
			$yMin = 200000000;
			foreach($this->series as $title=>$contents){
				$data = $contents['data'];
				foreach($data as $x=>$y){
					$xMax = ($x > $xMax)? $x : $xMax;
					$xMin = ($x < $xMin)? $x : $xMin;
					$yMax = ((int)$y > (int)$yMax)? $y : $yMax;
					$yMin = ((int)$y < (int)$yMin)? $y : $yMin;
				}
			}
			$this->xMax = $xMax;
			$this->xMin = $xMin;
			$this->yMax = $yMax;
			$this->yMin = $yMin;
		}
		
		public function draw($FPDF){
			$this->drawGrid($FPDF);
			foreach($this->series as $title=>$contents){
				$this->drawSeries($FPDF, $title, $contents);
			}
			$this->drawBoundingBox($FPDF);
			$this->drawAxis($FPDF);
		}
		
		private function drawAxis($FPDF){
			$FPDF->SetFont($this->axisFont);
			$FPDF->SetFontSize($this->axisFontSize);
			$this->drawXAxis($FPDF);
			$this->drawYAxis($FPDF);
		}
		
		private function drawXAxis($FPDF){
			if(isset($this->xLabels['labels'])){
				if(isset($this->xLabels['step']))
					$step = $this->xLabels['step'];
				else
					$step = 1;
				$xStart = $this->y + $this->height;
				$xEnd = $this->y + $this->height + $this->tickSize;
				$cellWidth = 2;
				$cellHeight = 2 * $this->tickSize;
				$i = 0;
				foreach($this->xLabels['labels'] as $x=>$label){
					if (($i % $step) == 0){
						$xPos = $this->calculateX($x);
						$FPDF->Line($xPos, $xStart, $xPos, $xEnd);
						$FPDF->SetXY(($xPos-$cellWidth/2),$xEnd);
						$FPDF->Cell($cellWidth, $cellHeight, $label, 0, 0, 'C');
					}
					$i++;
				}
			}
			else {
				$xStep = ($this->width - 2 * $this->xPadding)/($this->gridVerticalLines + 1);
				$xPos = $this->x + $this->xPadding;
				$xStart = $this->y + $this->height;
				$xEnd = $this->y + $this->height + $this->tickSize;
				$cellHeight = 1;
				for($i = 0; $i < ($this->gridVerticalLines + 2) ; $i++){
					$FPDF->Line($xPos, $xStart, $xPos, $xEnd);
					$FPDF->SetXY(($xPos-$xStep/2),$xEnd);
					$FPDF->Cell($xStep, $cellHeight, round($this->calculateXValue($xPos), 2), 0, 0, 'C');
					$xPos += $xStep;
				}
			}
		}
		
		private function drawYAxis($FPDF){
			if(isset($this->yLabels['labels'])){
				if(isset($this->yLabels['step']))
					$step = $this->yLabels['step'];
				else
					$step = 1;
				$yStart = $this->x - $this->tickSize;
				$yEnd = $this->x;
				$cellWidth = 1;
				$cellHeight = 2;
				$i = 0;
				foreach($this->yLabels['labels'] as $y=>$label){
					if (($i % $step) == 0){
						$yPos = $this->calculateY($y);
						$FPDF->Line($yStart, $yPos, $yEnd, $yPos);
						$FPDF->SetXY($yStart - $cellWidth, $yPos - $cellHeight/2);
						$FPDF->Cell($cellWidth, $cellHeight, $label, 0, 0, 'R');
					}
					$i++;
				}
			}
			else {
				$yStep = ($this->height - 2 * $this->yPadding)/($this->gridHorizontalLines + 1);
				$yPos = $this->y + $this->yPadding;
				$yStart = $this->x - $this->tickSize;
				$yEnd = $this->x;
				$cellWidth = 1;
				for($i = 0; $i < ($this->gridHorizontalLines + 2) ; $i++){
					$FPDF->Line($yStart, $yPos, $yEnd, $yPos);
					$FPDF->SetXY($yStart - $cellWidth, $yPos - $yStep/2);
					$FPDF->Cell($cellWidth, $yStep, round($this->calculateYValue($yPos), 2), 0, 0, 'R');
					$yPos += $yStep;
				}
			}
		}

		private function drawGrid($FPDF){
			$FPDF->setLineWidth($this->gridLineWidth);
			$FPDF->setDrawColor($this->gridColour[0], $this->gridColour[1], $this->gridColour[2]);
			$this->drawHorizontalLines($FPDF);
			$this->drawVerticalLines($FPDF);
		}
		
		private function drawSeries($FPDF, $title, $contents){
			if(isset($contents['data']))
				$data = $contents['data'];
			else{
				echo "FPDF Graphs: Serie no contiene datos";
				return;
			}	
			
			if(isset($contents['width']))
				$width = $contents['width'];
			else
				$width = 1;
				
			if(isset($contents['dot_size']))
				$dotSize = $contents['dot_size'];
			else
				$dotSize = 2;
				
			if(isset($contents['colour']))
				$colour = $contents['colour'];
			else
				$colour = array(0, 0, 0);
				
			if(isset($contents['type']))
				$type = $contents['type'];
			else
				$type = $this->type;
			
			$FPDF->setLineWidth($width);
			$FPDF->setDrawColor($colour[0], $colour[1], $colour[2]);
			$FPDF->setFillColor($colour[0], $colour[1], $colour[2]);
			
			switch($type){
				case 'line':
					reset($data);
					if(!(list($x0, $y0) = each($data))){
						echo "FPDF Graphs: Serie no contiene datos";
						return;
					}
					$x0 = $this->calculateX($x0);
					$y0 = $this->calculateY($y0);
					while(list($x1, $y1) = each($data)){
						$x1 = $this->calculateX($x1);
						$y1 = $this->calculateY($y1);
						$FPDF->Line($x0, $y0, $x1, $y1);
						$x0 = $x1;
						$y0 = $y1;
					}
					break;
				case 'line_dot':
					reset($data);
					if(!(list($x0, $y0) = each($data))){
						echo "FPDF Graphs: Serie no contiene datos";
						return;
					}
					$x0 = $this->calculateX($x0);
					$y0 = $this->calculateY($y0);
					while(list($x1, $y1) = each($data)){
						$x1 = $this->calculateX($x1);
						$y1 = $this->calculateY($y1);
						$FPDF->Line($x0, $y0, $x1, $y1);
						$FPDF->Circle($x0, $y0, $dotSize, 'F');
						$x0 = $x1;
						$y0 = $y1;
					}
					$FPDF->Circle($x0, $y0, $dotSize, 'F');
					break;
				case 'scatter':
					foreach($data as $x=>$y){
						$x = $this->calculateX($x);
						$y = $this->calculateY($y);
						$FPDF->Circle($x, $y, $dotSize, 'F');
					}
					break;
			}
				
		}
		
		private function calculateX($x){
			$range = ($this->width - 2 * $this->xPadding) / ($this->xMax - $this->xMin);
			return ($x - $this->xMin) * $range + $this->x + $this->xPadding;
		}
		
		private function calculateXValue($x){
			$range = ($this->width - 2 * $this->xPadding) / ($this->xMax - $this->xMin);
			return ($x - $this->x - $this->xPadding)/$range + $this->xMin;
		}
		
		private function calculateY($y){
			$range = ($this->height - 2 * $this->yPadding) / ($this->yMax - $this->yMin);
			return -($y - $this->yMin) * $range + $this->y + $this->height - $this->yPadding;
		}
		
		private function calculateYValue($y){
			$range = ($this->height - 2 * $this->yPadding) / ($this->yMax - $this->yMin);
			return ($this->y + $this->height - $this->yPadding - $y)/$range + $this->yMin;
		}
		
		private function drawBoundingBox($FPDF){
			$FPDF->setLineWidth($this->boxLineWidth);
			$FPDF->setDrawColor($this->boxColour[0], $this->boxColour[1], $this->boxColour[2]);
			$FPDF->Rect($this->x, $this->y, $this->width, $this->height);
		}
		
		private function drawVerticalLines($FPDF){
			if(isset($this->xLabels['labels'])){
				$xStart = $this->y;
				$xEnd = $this->y + $this->height;
				foreach($this->xLabels['labels'] as $x=>$label){
					$xPos = $this->calculateX($x);
					$FPDF->Line($xPos, $xStart, $xPos, $xEnd);
				}
			}
			else {
				$xStep = ($this->width - 2 * $this->xPadding)/($this->gridVerticalLines + 1);
				$xPos = $this->x + $this->xPadding;
				$xStart = $this->y;
				$xEnd = $this->y + $this->height;				
				for($i = 0; $i < ($this->gridVerticalLines + 2) ; $i++){
					$FPDF->Line($xPos, $xStart, $xPos, $xEnd);
					$xPos += $xStep;
				}
			}
		}
		
		private function drawHorizontalLines($FPDF){
			if(isset($this->yLabels['labels'])){
				$yStart = $this->x;
				$yEnd = $this->x + $this->width;
				foreach($this->yLabels['labels'] as $y=>$label){
					$yPos = $this->calculateY($y);
					$FPDF->Line($yStart, $yPos, $yEnd, $yPos);
				}
			}
			else {
				$yStep = ($this->height - 2 * $this->yPadding)/($this->gridHorizontalLines + 1);
				$yPos = $this->y + $this->yPadding;
				$yStart = $this->x;
				$yEnd = $this->x + $this->width;
				for($i = 0; $i < ($this->gridHorizontalLines + 2) ; $i++){
					$FPDF->Line($yStart, $yPos, $yEnd, $yPos);
					$yPos += $yStep;
				}
			}
		}

	}

?>
